﻿/*
 * Toast8
 * All rights reserved
 * Copyright (C) 2014 Peter Nylander
 */

using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Toast8.Common;
using Toast8.SpotifyHelper;

namespace Toast8
{
    public class Toast8ViewModel : INotifyPropertyChanged, IDisposable
    {
        internal static Toast8ViewModel Current { get; private set; }

        private DispatcherTimer timer;

        private Status CurrentStatus;
        private Status PreviousStatus;

        private SpotifyWebHelperApiClient spotifyClient;

        public EventHandler<Status> OnStateChange;
        public EventHandler<Status> OnTrackChange;
        public EventHandler<Status> OnVolumeChange;

        private List<HotKey> GlobalHotKeys;

        private ImageCache ImageCache;

        public Toast8ViewModel()
        {
            Current = this;

            // Initialize hotkeys
            // Read from configuration file or change in settings
            GlobalHotKeys = new List<HotKey>
            {
                new HotKey(Key.Insert, KeyModifier.Ctrl | KeyModifier.Alt, OnGlobalHotKey, PlayerAppCommand.Play), // Play
                new HotKey(Key.PageUp, KeyModifier.Ctrl | KeyModifier.Alt, OnGlobalHotKey, PlayerAppCommand.PreviousTrack), // Previous
                new HotKey(Key.PageDown, KeyModifier.Ctrl | KeyModifier.Alt, OnGlobalHotKey, PlayerAppCommand.NextTrack), // Next
                new HotKey(Key.End, KeyModifier.Ctrl | KeyModifier.Alt, OnGlobalHotKey, PlayerAppCommand.Stop) // Stop
            };
        }

        public async Task Initialize()
        {
            // In order to display toasts, a desktop application must have a shortcut on the Start menu.
            ToastHelper.TryCreateShortcut();

            // Settings path
            string settingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Toast8");
            if (!Directory.Exists(settingsPath))
            {
                Directory.CreateDirectory(settingsPath);
            }
            string cachePath = Path.Combine(settingsPath, "Cache");

            ImageCache = new ImageCache(cachePath);

            // Listen to events
            OnStateChange += HandleOnStateChange;
            OnTrackChange += HandleOnTrackChange;

            CurrentStatus = new Status();

            // Initialize client
            this.spotifyClient = new SpotifyWebHelperApiClient();

            // Check if SpotifyWebHelper is running
            if (spotifyClient.IsAvailable())
            {
                await this.spotifyClient.Initialize();
                string version = await spotifyClient.GetVersion();

                this.CurrentStatus = await spotifyClient.GetCurrentStatus();
            }
        }

        public void Start()
        {
            // Start timer
            timer = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, OnTimerTick, Application.Current.Dispatcher);
        }

        private async void OnTimerTick(object sender, EventArgs e)
        {
            if (!spotifyClient.IsAvailable())
            {
                return;
            }
            else if (!spotifyClient.Initialized)
            {
                await spotifyClient.Initialize();
            }

            CurrentStatus = await spotifyClient.GetCurrentStatus();
            if (CurrentStatus.Error != null)
            {
                // Something is wrong
                if (CurrentStatus.Error.Type == "4107")
                {
                    // Invalid token
                    await this.spotifyClient.Initialize();
                }
                return;
            }

            if (PreviousStatus == null)
            {
                // First tick
                if (OnStateChange != null) OnStateChange(this, CurrentStatus);
                PreviousStatus = CurrentStatus;
                return;
            }

            // Check playing state
            if (CurrentStatus.Playing != PreviousStatus.Playing)
            {
                if (OnStateChange != null) OnStateChange(this, CurrentStatus);
            }

            // Check track changes
            if (CurrentStatus.Track != null &&
                CurrentStatus.Track.TrackResource.Uri != PreviousStatus.Track.TrackResource.Uri)
            {
                if (OnTrackChange != null) OnTrackChange(this, CurrentStatus);
            }

            // Check volume changes
            if (CurrentStatus.Volume != PreviousStatus.Volume)
            {
                if (OnVolumeChange != null) OnVolumeChange(this, CurrentStatus);
            }

            PreviousStatus = CurrentStatus;
        }

        public async void HandleOnStateChange(object sender, Status arg)
        {
            Debug.WriteLine("OnStateChanged: Playing=" + arg.Playing.ToString());

            if (arg.Track != null && arg.Track.TrackResource != null)
            {
                string text1 = string.Empty;
                string text2 = string.Empty;
                if (arg.Playing == false)
                {
                    text1 = "Paused";
                    text2 = arg.Track.ArtistResource.Name + " - " + arg.Track.TrackResource.Name;
                }
                else
                {
                    text1 = arg.Track.TrackResource.Name;
                    text2 = arg.Track.ArtistResource.Name;
                }

                // Get image from cache
                string imagePath = await ImageCache.Get(arg.Track.AlbumResource, CoverArtSize.Size120);

                // Show toast
                ToastHelper.ShowToastNotification(true, imagePath, text1, text2, null);
            }

            NotifyProperties();
        }

        public async void HandleOnTrackChange(object sender, Status arg)
        {
            Debug.WriteLine("OnTrackChange: " + arg.Track.ArtistResource.Name + " - " + arg.Track.TrackResource.Name);

            // Get image from cache
            string imagePath = await ImageCache.Get(arg.Track.AlbumResource, CoverArtSize.Size120);

            ToastHelper.ShowToastNotification(true, imagePath, arg.Track.TrackResource.Name, arg.Track.ArtistResource.Name, null);

            NotifyProperties();
        }

        public void NotifyProperties()
        {
            OnPropertyChanged("Album");
            OnPropertyChanged("Artist");
            OnPropertyChanged("Artwork");
            OnPropertyChanged("Title");
            OnPropertyChanged("NextEnabled");
            OnPropertyChanged("PreviousEnabled");
            OnPropertyChanged("PlayButtonContent");
        }

        #region Commands

        /// <summary>
        /// Starts playing or pauses player
        /// </summary>
        public ICommand PlayCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = async () =>
                    {
                        if (CurrentStatus.Playing)
                        {
                            await spotifyClient.Pause();
                        }
                        else
                        {
                            await spotifyClient.Resume();
                        }
                    }
                };
            }
        }

        /// <summary>
        /// Starts playing or pauses player
        /// </summary>
        public ICommand PauseCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = async () =>
                    {
                        await spotifyClient.Pause();
                    }
                };
            }
        }

        /// <summary>
        /// Stop player
        /// </summary>
        public ICommand StopCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.Stop);
                    }
                };
            }
        }

        /// <summary>
        /// Change to previous track
        /// </summary>
        public ICommand PreviousCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.PreviousTrack);
                    }
                };
            }
        }

        /// <summary>
        /// Change to next track
        /// </summary>
        public ICommand NextCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.NextTrack);
                    }
                };
            }
        }

        public ICommand VolumeDownCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.VolumeDown);
                    }
                };
            }
        }

        public ICommand VolumeUpCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.VolumeUp);
                    }
                };
            }
        }

        public ICommand MuteCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        SpotifyAppHelper.SendCommand(PlayerAppCommand.Mute);
                    }
                };
            }
        }

        public ICommand ShowToastCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => spotifyClient.IsAvailable(),
                    CommandAction = () =>
                    {
                        ToastHelper.ShowToastNotification(true, "Toast8.png", "Test", "Test", "Test");
                    }
                };
            }
        }

        /// <summary>
        /// Shows a window, if none is already open.
        /// </summary>
        public ICommand ShowWindowCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CanExecuteFunc = () => { return true; }, //Application.Current.MainWindow == null,
                    CommandAction = () =>
                    {
                        if (Application.Current.MainWindow == null)
                        {
                            Application.Current.MainWindow = new MainWindow();
                        }
                        Application.Current.MainWindow.Show();
                    }
                };
            }
        }

        /// <summary>
        /// Hides the main window. This command is only enabled if a window is open.
        /// </summary>
        public ICommand HideWindowCommand
        {
            get
            {
                return new DelegateCommand
                {
                    CommandAction = () => Application.Current.MainWindow.Hide(),
                    CanExecuteFunc = () => Application.Current.MainWindow != null
                };
            }
        }

        /// <summary>
        /// Shuts down the application.
        /// </summary>
        public ICommand ExitApplicationCommand
        {
            get
            {
                return new DelegateCommand { CommandAction = () => Application.Current.Shutdown() };
            }
        }

        #endregion

        public string Artwork
        {
            get
            {
                if (CurrentStatus.Online)
                {
                    if (CurrentStatus.Track.ArtistResource.Name != null)
                    {
                        return Path.GetFullPath(ImageCache.GetLargeArtwork(CurrentStatus.Track.AlbumResource));
                    }
                }
                return Path.GetFullPath("../../Assets/Toast8.png");
            }
            set
            {
                Artwork = value;
                OnPropertyChanged();
            }
        }

        public string Title
        {
            get
            {
                if (CurrentStatus.Online)
                {
                    if (CurrentStatus.Track.TrackResource.Name != null)
                    {
                        return CurrentStatus.Track.TrackResource.Name;
                    }
                }
                return string.Empty;
            }
            set
            {
                Title = value;
                OnPropertyChanged();
            }
        }

        public string Album
        {
            get
            {
                if (CurrentStatus.Online)
                {
                    if (CurrentStatus.Track.AlbumResource.Name != null)
                    {
                        return CurrentStatus.Track.AlbumResource.Name;
                    }
                }
                return string.Empty;
            }
            set
            {
                Album = value;
                OnPropertyChanged();
            }
        }

        public string Artist
        {
            get
            {
                if (CurrentStatus.Online)
                {
                    if (CurrentStatus.Track.ArtistResource.Name != null)
                    {
                        return CurrentStatus.Track.ArtistResource.Name;
                    }
                }
                return string.Empty;
            }
            set
            {
                Artist = value;
                OnPropertyChanged();
            }
        }

        public string PlayButtonContent
        {
            get
            {
                if (CurrentStatus.Online)
                {
                    if (CurrentStatus.Playing)
                    {
                        return "\uE103";
                    }
                }
                return "\uE102";
            }
            set
            {
                PlayButtonContent = value;
                OnPropertyChanged();
            }
        }

        public bool PreviousEnabled { get { return CurrentStatus.PreviousEnabled; } }

        public bool NextEnabled { get { return CurrentStatus.NextEnabled; } }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        public void Dispose()
        {
            foreach (HotKey k in GlobalHotKeys)
            {
                k.Dispose();
            }
        }

        void OnGlobalHotKey(HotKey hotKey)
        {
            ICommand command = null;

            switch (hotKey.PlayerAppCommand)
            {
                case PlayerAppCommand.Play:
                case PlayerAppCommand.Pause:
                    command = PlayCommand;
                    break;
                case PlayerAppCommand.PreviousTrack:
                    command = PreviousCommand;
                    break;
                case PlayerAppCommand.NextTrack:
                    command = NextCommand;
                    break;
                case PlayerAppCommand.Stop:
                    command = StopCommand;
                    break;
                case PlayerAppCommand.Mute:
                    command = MuteCommand;
                    break;
                case PlayerAppCommand.VolumeDown:
                    command = VolumeDownCommand;
                    break;
                case PlayerAppCommand.VolumeUp:
                    command = VolumeUpCommand;
                    break;
            }

            if (command != null && command.CanExecute(null))
            {
                command.Execute(null);
            }
        }
    }
}
